package com.tbit.uqbike.service.base.impl;

import com.tbit.common.entity.GPS.CarHistory;
import com.tbit.uqbike.constant.MapConstant;
import com.tbit.uqbike.constant.SysConstant;
import com.tbit.uqbike.enums.ParkPointType;
import com.tbit.uqbike.object.business.Point;
import com.tbit.uqbike.object.pojo.*;
import com.tbit.uqbike.object.pojo.statistics.ExportProhibitArea;
import com.tbit.uqbike.service.base.CalibrateService;
import com.tbit.uqbike.webmanager.util.MapUtils;
import com.tbit.uqbike.webmanager.util.calibreate.GpsCorrect;
import com.tbit.uqbike.webmanager.util.calibreate.GpsReverseCorrect;
import com.tbit.utils.GeoUtil;
import org.springframework.stereotype.Service;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

@Service("calibrateService")
public class CalibrateServiceImpl implements CalibrateService {
    /**
     * 经纬度格式化
     */
    private DecimalFormat df = new DecimalFormat("#.######");

    @Override
    public void calibrateOffSet(TerPosition terPosition, int mapType) {
        double lng = terPosition.getLon();
        double lat = terPosition.getLat();

        Point p = calibrateOffSet(lng, lat, mapType);
        terPosition.setLonC(Double.parseDouble(df.format(p.getX())));
        terPosition.setLatC(Double.parseDouble(df.format(p.getY())));
    }

    @Override
    public void calibrateOffSet(List<TerPosition> terPositions, int mapType) {
        for (int i = 0; i < terPositions.size(); i++) {
            TerPosition terPosition = terPositions.get(i);
            double lng = terPosition.getLon();
            double lat = terPosition.getLat();

            Point p = calibrateOffSet(lng, lat, mapType);
            terPosition.setLonC(Double.parseDouble(df.format(p.getX())));
            terPosition.setLatC(Double.parseDouble(df.format(p.getY())));
        }

    }

    /**
     * 获取校准点
     */
    @Override
    public Point calibrateOffSet(double lo, double la, int mapType) {
        Point point = new Point(lo, la);
        if (MapConstant.MAP_BAIDU == mapType) {
            double[] latlng = GpsCorrect.oriToBaidu(la, lo);
            point.setX(latlng[1]);
            point.setY(latlng[0]);
        } else if (MapConstant.MAP_GOOGLE == mapType) {
            double[] latlng = GpsCorrect.oriToGoogle(la, lo);
            point.setX(latlng[1]);
            point.setY(latlng[0]);
        }

        return point;
    }


    /**
     * 获取逆向校准点
     */
    public Point calibrateRecerseOffSet(double lo, double la, int mapType) {
        Point point = new Point(lo, la);
        if (MapConstant.MAP_BAIDU == mapType) {
            double[] latlng = GpsReverseCorrect.baiduToOri(la, lo);
            point.setX(latlng[1]);
            point.setY(latlng[0]);
        } else if (MapConstant.MAP_GOOGLE == mapType) {
            double[] latlng = GpsReverseCorrect.googleToOri(la, lo);
            point.setX(latlng[1]);
            point.setY(latlng[0]);
        }

        return point;
    }

    @Override
    public void celibrateParkPoint(List<ParkPoint> parkPoints, int mapType) {
        for (int i = 0; i < parkPoints.size(); i++) {
            ParkPoint parkPoint = parkPoints.get(i);
            double lng = parkPoint.getLo();
            double lat = parkPoint.getLa();
            Point p = calibrateOffSet(lng, lat, mapType);
            parkPoint.setLoC(Double.parseDouble(df.format(p.getX())));
            parkPoint.setLaC(Double.parseDouble(df.format(p.getY())));
        }
    }


    @Override
    public void celibrateGeoRegion(List<GeoRegion> geoRegions, int mapType) {
        for (int i = 0; i < geoRegions.size(); i++) {
            GeoRegion geoRegion = geoRegions.get(i);
            double lng = geoRegion.getCenterLon();
            double lat = geoRegion.getCenterLat();
            Point p = calibrateOffSet(lng, lat, mapType);
            geoRegion.setCenterLon(Double.parseDouble(df.format(p.getX())));
            geoRegion.setCenterLat(Double.parseDouble(df.format(p.getY())));
        }
    }

    @Override
    public void celibrateProhibit(List<ProhibitArea> prohibitAreas, int mapType) {
        for (int i = 0; i < prohibitAreas.size(); i++) {
            ProhibitArea prohibitArea = prohibitAreas.get(i);
            double lng = prohibitArea.getLo();
            double lat = prohibitArea.getLa();
            Point p = calibrateOffSet(lng, lat, mapType);
            prohibitArea.setLoC(Double.parseDouble(df.format(p.getX())));
            prohibitArea.setLaC(Double.parseDouble(df.format(p.getY())));
            /*List<Point> points = GeoUtil.getPoints(prohibitArea.getPoints());
            List<Point> pointCs = new ArrayList<Point>();
            for (Point point : points) {
                pointCs.add(commonCalibrate(point, MapConstant.MAP_ORI, mapType));
            }
            prohibitArea.setPointsC(GeoUtil.getPointToString(pointCs));*/
        }
    }

    /**
     * 经纬度校准
     */
    public Point calibrate(Point point, Integer mapType) {
        Point cP = calibrateOffSet(point.getX(), point.getY(), mapType);
        return new Point(Double.parseDouble(df.format(cP.getX())), Double.parseDouble(df.format(cP.getY())));
    }

    /**
     * 百度坐标转原始坐标
     */
    public Point baiduToOrigial(Point point) {
        Point bP = calibrateRecerseOffSet(point.getX(), point.getY(), SysConstant.BAIDU);
        return new Point(Double.parseDouble(df.format(bP.getX())), Double.parseDouble(df.format(bP.getY())));
    }

    /**
     * 谷歌坐标转原始坐标
     */
    public Point googleToOrigial(Point point) {
        Point bP = calibrateRecerseOffSet(point.getX(), point.getY(), SysConstant.GOOGLE);
        return new Point(Double.parseDouble(df.format(bP.getX())), Double.parseDouble(df.format(bP.getY())));
    }

    @Override
    public Point commonCalibrate(Point point, Integer from, Integer to) {
        if (from == SysConstant.ORIGINAL && to == SysConstant.BAIDU) {
            /*原始到百度*/
            point = calibrate(point, SysConstant.BAIDU);
        } else if (from == SysConstant.ORIGINAL && to == SysConstant.GOOGLE) {
            /*原始到谷歌*/
            point = calibrate(point, SysConstant.GOOGLE);
        } else if (from == SysConstant.BAIDU && to == SysConstant.ORIGINAL) {
            /*百度到原始*/
            point = baiduToOrigial(point);
        } else if (from == SysConstant.GOOGLE && to == SysConstant.ORIGINAL) {
            /*谷歌到原始*/
            point = googleToOrigial(point);
        }
        return point;
    }

    @Override
    public void celibrateParkPointApply(List<ParkPointApply> parkPointApplys, int mapType) {
        for (int i = 0; i < parkPointApplys.size(); i++) {
            ParkPointApply parkPointApply = parkPointApplys.get(i);
            double lng = parkPointApply.getLo();
            double lat = parkPointApply.getLa();

            Point p = calibrateOffSet(lng, lat, mapType);
            parkPointApply.setLoC(Double.parseDouble(df.format(p.getX())));
            parkPointApply.setLaC(Double.parseDouble(df.format(p.getY())));
        }
    }

    @Override
    public void calGeo(Geo geo, Integer oriMapType, Integer targetMapType) {
        List<Point> points = GeoUtil.getPoints(geo.getPoints());
        List<Point> pointCs = new ArrayList<>();
        for (Point point : points) {
            pointCs.add(commonCalibrate(point, oriMapType, targetMapType));
        }
        if (targetMapType.equals(MapConstant.MAP_ORI)) {
            //从数据库获取
            geo.setPointsC(geo.getPoints());
            geo.setPoints(GeoUtil.getPointToString(pointCs));
        } else {
            //保存到数据库
            geo.setPointsC(GeoUtil.getPointToString(pointCs));
        }
    }

    @Override
    public void calProhibitArea(ProhibitArea prohibitArea, Integer oriMapType,
                                Integer targetMapType) {
        //多边行停车点
        List<Point> points = GeoUtil.getPoints(prohibitArea.getPoints());
        List<Point> pointCs = new ArrayList<Point>();
        for (Point point : points) {
            pointCs.add(commonCalibrate(point, oriMapType, targetMapType));
        }
        if (targetMapType.equals(MapConstant.MAP_ORI)) {
            //保存到数据库
            prohibitArea.setPointsC(prohibitArea.getPoints());
            prohibitArea.setPoints(GeoUtil.getPointToString(pointCs));
        } else {
            prohibitArea.setPointsC(GeoUtil.getPointToString(pointCs));
        }
    }

    @Override
    public void calProhibitArea(ExportProhibitArea exportProhibitArea, Integer mapType) {
        if (MapConstant.MAP_ORI.equals(mapType)) {
            exportProhibitArea.setExportPoints(exportProhibitArea.getPoints());
            exportProhibitArea.setExportLo(exportProhibitArea.getLo());
            exportProhibitArea.setExportLa(exportProhibitArea.getLa());
        } else {
            Point point = calibrateOffSet(exportProhibitArea.getLo(), exportProhibitArea.getLa(), mapType);
            exportProhibitArea.setExportLo(point.getX());
            exportProhibitArea.setExportLa(point.getY());

            List<Point> points = GeoUtil.getPoints(exportProhibitArea.getPoints());
            List<Point> exportPoints = new ArrayList<Point>();
            for (Point pointItem : points) {
                exportPoints.add(commonCalibrate(pointItem, MapConstant.MAP_ORI, MapConstant.MAP_GOOGLE));
            }
            exportProhibitArea.setExportPoints(GeoUtil.getPointToString(exportPoints));
        }
    }

    @Override
    public List<ParkPointApply> calibrateParkPointApplyAndRemove(List<ParkPointApply> list, Integer mapType, Double lat, Double lon, Integer distance) {
        List<ParkPointApply> resultList = new LinkedList<>();
        for (ParkPointApply parkPointApply : list) {
            Point p = calibrateOffSet(parkPointApply.getLo(), parkPointApply.getLa(), mapType);
            parkPointApply.setLoC(Double.parseDouble(df.format(p.getX())));
            parkPointApply.setLaC(Double.parseDouble(df.format(p.getY())));
            if (MapUtils.distance(lon, lat, parkPointApply.getLoC(), parkPointApply.getLaC()) <= distance) {
                resultList.add(parkPointApply);
            }
        }
        return resultList;
    }

    @Override
    public void calDetentionArea(DetentionArea detentionArea, Integer oriMapType, Integer targetMapType) {
        //多边形电子围栏
        if (ParkPointType.PARKPOINT_POLYGON_TYPE.getType().equals(detentionArea.getType().toString())) {
            List<Point> points = GeoUtil.getPoints(detentionArea.getPoints());
            //计算中心点原始
            Point pointGoogle = GeoUtil.GetCenterPoint(points);
            detentionArea.setLoC(pointGoogle.getX());
            detentionArea.setLaC(pointGoogle.getY());
            List<Point> pointCs = new ArrayList<>();
            for (Point point : points) {
                pointCs.add(commonCalibrate(point, oriMapType, targetMapType));
            }
            detentionArea.setPoints(GeoUtil.getPointToString(pointCs));
            //计算中心点原始
            Point point = GeoUtil.GetCenterPoint(pointCs);
            detentionArea.setLo(point.getX());
            detentionArea.setLa(point.getY());
        } else {
            /*校准经纬度为原始经纬度*/
            Point point = new Point(detentionArea.getLo(), detentionArea.getLa());
            point = commonCalibrate(point, oriMapType, targetMapType);
            if (point != null) {
                detentionArea.setLo(point.getX());
                detentionArea.setLa(point.getY());
            }
        }
    }

    @Override
    public void calibrateForbidden(Forbidden forbidden, Integer fromMapType, Integer toMapType, boolean isSave) {
        //转换前经纬度
        List<Point> points = GeoUtil.getPoints(isSave ? forbidden.getPointsC() : forbidden.getPoints());
        //转换后经纬度
        List<Point> pointCs = new LinkedList<>();
        for (Point point : points) {
            pointCs.add(commonCalibrate(point, fromMapType, toMapType));
        }
        if (isSave) {
            forbidden.setPoints(GeoUtil.getPointToString(pointCs)).setUpdateTime(new Date());
            //保存到数据库,设置中心点
            Point centerPoint = GeoUtil.GetCenterPoint(pointCs);
            forbidden.setCenterLo(centerPoint.getX()).setCenterLa(centerPoint.getY());
            //pointsC转换为默认的谷歌——高德——腾讯经纬度
            if (!MapConstant.MAP_GOOGLE.equals(fromMapType)) {
                pointCs.clear();
                for (Point point : points) {
                    pointCs.add(commonCalibrate(point, fromMapType, MapConstant.MAP_GOOGLE));
                }
                forbidden.setPointsC(GeoUtil.getPointToString(pointCs));
            }
        } else {
            forbidden.setPointsC(GeoUtil.getPointToString(pointCs));
            //从数据库获取,校准中心点
            Point point = commonCalibrate(new Point(forbidden.getCenterLo(), forbidden.getCenterLa()), fromMapType, toMapType);
            forbidden.setCenterLoC(point.getX()).setCenterLaC(point.getY());
        }

    }

    @Override
    public <T> void calVert(T t, Integer oriMapType, Integer targetMapType) {
        if (t instanceof ParkPointVert) {
            ParkPointVert parkPointVert = (ParkPointVert) t;
            List<Point> points = GeoUtil.getPoints(parkPointVert.getPoints());
            List<Point> pointCs = new ArrayList<Point>();
            for (Point point : points) {
                pointCs.add(commonCalibrate(point, oriMapType, targetMapType));
            }
            if (oriMapType.equals(MapConstant.MAP_ORI)) {
                //从数据库获取
                parkPointVert.setPointsC(GeoUtil.getPointToString(pointCs));
            } else {
                //保存到数据库
                parkPointVert.setPoints(GeoUtil.getPointToString(pointCs));
            }
        } else if (t instanceof GeoRegion) {
            GeoRegion parkPointVert = (GeoRegion) t;
            List<Point> points = GeoUtil.getPoints(parkPointVert.getPoints());
            List<Point> pointCs = new ArrayList<Point>();
            for (Point point : points) {
                pointCs.add(commonCalibrate(point, oriMapType, targetMapType));
            }
            if (oriMapType.equals(MapConstant.MAP_ORI)) {
                //从数据库获取
                parkPointVert.setPoints(GeoUtil.getPointToString(pointCs));
            } else {
                //保存到数据库
                parkPointVert.setPoints(GeoUtil.getPointToString(pointCs));
            }
        }

    }

    @Override
    public void calParkPoint(ParkPoint parkPoint, Integer oriMapType, Integer targetMapType) {
        if (ParkPointType.PARKPOINT_POLYGON_TYPE.getType().equals(String.valueOf(parkPoint.getParkPointType()))) {
            //多边行停车点
            List<Point> points = GeoUtil.getPoints(parkPoint.getPoints());
            List<Point> pointCs = new ArrayList<Point>();
            for (Point point : points) {
                pointCs.add(commonCalibrate(point, oriMapType, targetMapType));
            }
            if (targetMapType.equals(MapConstant.MAP_ORI)) {
                //保存到数据库
                parkPoint.setPointsC(parkPoint.getPoints());
                parkPoint.setPoints(GeoUtil.getPointToString(pointCs));
            } else {
                parkPoint.setPointsC(GeoUtil.getPointToString(pointCs));
            }
        } else {
            //圆形停车点
            Point point = new Point(parkPoint.getLo(), parkPoint.getLa());
            point = commonCalibrate(point, oriMapType, targetMapType);
            if (point != null) {
                if (targetMapType.equals(MapConstant.MAP_ORI)) {
                    //保存到数据库
                    parkPoint.setLo(point.getX());
                    parkPoint.setLa(point.getY());
                } else {
                    parkPoint.setLoC(point.getX());
                    parkPoint.setLaC(point.getY());
                }

            }
        }
    }

    @Override
    public void calibrateWareHouse(List<Warehouse> warehouses, int mapType) {
        for (int i = 0; i < warehouses.size(); i++) {
            Warehouse warehouse = warehouses.get(i);
            double lng = warehouse.getLo();
            double lat = warehouse.getLa();

            Point p = calibrateOffSet(lng, lat, mapType);
            warehouse.setLoC(Double.parseDouble(df.format(p.getX())));
            warehouse.setLaC(Double.parseDouble(df.format(p.getY())));
        }
    }

    @Override
    public void calWarehouse(Warehouse warehouse, Integer oriMapType, Integer targetMapType) {
        /*校准经纬度为原始经纬度*/
        Point point = new Point(warehouse.getLo(), warehouse.getLa());
        point = commonCalibrate(point, oriMapType, targetMapType);
        if (point != null) {
            warehouse.setLo(point.getX());
            warehouse.setLa(point.getY());
        }
    }

    @Override
    public void calibrateWareHouseSin(Warehouse warehouse, int mapType) {
        double lng = warehouse.getLo();
        double lat = warehouse.getLa();
        Point p = calibrateOffSet(lng, lat, mapType);
        warehouse.setLoC(Double.parseDouble(df.format(p.getX())));
        warehouse.setLaC(Double.parseDouble(df.format(p.getY())));

    }

    @Override
    public void calibrateGPSLastPos(List<CarHistory> carHistories, Integer mapType) {
        for (CarHistory carHistory : carHistories) {
            double lng = carHistory.getLon();
            double lat = carHistory.getLat();
            Point p = calibrateOffSet(lng, lat, mapType);
            carHistory.setLon_c(Double.parseDouble(df.format(p.getX())));
            carHistory.setLat_c(Double.parseDouble(df.format(p.getY())));
        }
    }

    @Override
    public void calibrateLastPos(List<AccountUserLastPos> accountUserLastPos, Integer mapType) {
        for (AccountUserLastPos lastPos : accountUserLastPos) {
            double lng = lastPos.getLon();
            double lat = lastPos.getLat();

            Point p = calibrateOffSet(lng, lat, mapType);
            lastPos.setLoC(Double.parseDouble(df.format(p.getX())));
            lastPos.setLaC(Double.parseDouble(df.format(p.getY())));
        }
    }


}